﻿#region Copyright Statement
//  ------------------------------------------------------------------------------
//  
//    This file is part of The Streambolics Library.
//    Copyright © 2005-2009, Stephan Leclercq & Streambolics
//    All rights reserved.
//  
//    The Streambolics Library is free software; you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation; either version 2 of the License, or
//    (at your option) any later version.
//  
//    The Streambolics Library is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//  
//    You should have received a copy of the GNU General Public License
//    along with Streambolics; if not, write to the Free Software
//    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//  
//  ------------------------------------------------------------------------------
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

namespace Streambolics.Hp41s
{
    public class Flags : Data
    {
        private bool[] _Flags = new bool[56];
        NumberFormatInfo _NumberFormatInfo = null;
        string _NumberFormatString = null;

        public Flags ()
            : base ("Flags")
        {
            // TODO : initialize known flags
        }

        public bool this[int f]
        {
            get
            {
                NonexistentException.Check (f, ReferenceType.Flag, 0, 55);
                return _Flags[f];
            }
            set
            {
                NonexistentException.Check (f, ReferenceType.Flag, 0, 30);
                SetFlag (f, value);

            }
        }

        private void SetFlag (int f, bool value)
        {
            if (_Flags[f] != value)
            {
                _Flags[f] = value;
                switch (f)
                {
                    case 0:
                    case 1:
                    case 2:
                    case 3:
                    case 4:
                        DoDisplayableChanged ();
                        break;

                    case FlagReference.UserMode :
                        DoDisplayableChanged();
                        DoKeyboardLayoutChanged();
                        break;

                    case FlagReference.DecimalPoint :
                    case FlagReference.DigitGrouping :
                        DoNumberFormatChanged ();
                        break;

                }
            }
        }

        public event EventHandler DisplayableChanged;

        protected virtual void OnDisplayableChanged (EventArgs e)
        {
            if (DisplayableChanged != null)
            {
                DisplayableChanged (this, e);
            }
        }

        protected void DoDisplayableChanged ()
        {
            OnDisplayableChanged (EventArgs.Empty);
        }

        public event EventHandler NumberFormatChanged;

        protected virtual void OnNumberFormatChanged (EventArgs e)
        {
            _NumberFormatInfo = null;
            _NumberFormatString = null;
            if (NumberFormatChanged != null)
            {
                NumberFormatChanged (this, e);
            }
        }

        protected void DoNumberFormatChanged ()
        {
            OnNumberFormatChanged (EventArgs.Empty);
        }

        public event EventHandler KeyboardLayoutChanged;

        protected virtual void OnKeyboardLayoutChanged (EventArgs e)
        {
            if (KeyboardLayoutChanged != null)
            {
                KeyboardLayoutChanged (this, e);
            }
        }

        protected void DoKeyboardLayoutChanged ()
        {
            OnKeyboardLayoutChanged (EventArgs.Empty);
        }

        /// <summary>
        ///     Implementation of the SF xx instruction (Set flag)
        /// </summary>
        /// <param name="f">
        ///     The flag number
        /// </param>

        public void Sf (int f)
        {
            this[f] = true;
        }

        /// <summary>
        ///     Implementation of the CF xx instruction (Clear flag)
        /// </summary>
        /// <param name="f">
        ///     The flag number
        /// </param>

        public void Cf (int f)
        {
            this[f] = false;
        }

        /// <summary>
        ///     Implementation of the XF xx instruction (Exchange flag)
        /// </summary>
        /// <param name="f">
        ///     The flag number
        /// </param>

        public void Xf (int f)
        {
            this[f] = !this[f];
        }
        /// <summary>
        ///     Implementation of the FS? xx instruction (Check flag set)
        /// </summary>
        /// <param name="f">
        ///     The flag number
        /// </param>

        public bool Fs (int f)
        {
            return this[f];
        }

        /// <summary>
        ///     Implementation of the FC? xx instruction (Check flag clear)
        /// </summary>
        /// <param name="f">
        ///     The flag number
        /// </param>

        public bool Fc (int f)
        {
            return !this[f];
        }

        /// <summary>
        ///     Implementation of the FS?C xx instruction (Check flag set, then clear)
        /// </summary>
        /// <param name="f">
        ///     The flag number
        /// </param>

        public bool FsC (int f)
        {
            return GetSet (f, false);
        }

        /// <summary>
        ///     Implementation of the FC?C xx instruction
        /// </summary>
        /// <param name="f">
        ///     The flag number
        /// </param>

        public bool FcC (int f)
        {
            return !GetSet (f, false);
        }

        /// <summary>
        ///     Implementation of the FS?S xx instruction
        /// </summary>
        /// <param name="f">
        ///     The flag number
        /// </param>

        public bool FsS (int f)
        {
            return GetSet (f, true);
        }

        /// <summary>
        ///     Implementation of the FC?S xx instruction
        /// </summary>
        /// <param name="f">
        ///     The flag number
        /// </param>

        public bool FcS (int f)
        {
            return !GetSet (f, true);
        }

        /// <summary>
        ///     Implementation of the FS?X xx instruction
        /// </summary>
        /// <param name="f">
        ///     The flag number
        /// </param>

        public bool FsX (int f)
        {
            Xf (f);
            return Fc (f);
        }

        /// <summary>
        ///     Implementation of the FC?X xx instruction
        /// </summary>
        /// <param name="f">
        ///     The flag number
        /// </param>

        public bool FcX (int f)
        {
            Xf (f);
            return Fs (f);
        }

        private bool GetSet (int f, bool newVal)
        {
            bool b = this[f];
            this[f] = newVal;
            return b;
        }

        /// <summary>
        ///     Present a numerical value according to the flags
        /// </summary>
        /// <param name="aValue">
        ///     The value to represent.
        /// </param>
        /// <returns>
        ///     A string representation of the value, as a 41 would
        ///     display it.
        /// </returns>

        public string Present (double aValue)
        {
            return aValue.ToString (NumberFormatString, NumberFormatInfo);
        }

        public NumberFormatInfo NumberFormatInfo
        {
            get
            {
                if (_NumberFormatInfo == null)
                {
                    _NumberFormatInfo = new NumberFormatInfo ();
                    _NumberFormatInfo.NegativeSign = "-";
                    _NumberFormatInfo.NumberDecimalDigits = 4;
                    _NumberFormatInfo.NumberDecimalSeparator = _Flags[FlagReference.DecimalPoint] ? "," : ".";
                    _NumberFormatInfo.NumberGroupSeparator = _Flags[FlagReference.DigitGrouping] ? (_Flags[FlagReference.DecimalPoint] ? "." : ",") : "";
                    _NumberFormatInfo.NumberGroupSizes = new int[] { 3 };
                    _NumberFormatInfo.PositiveSign = "";
                }
                return _NumberFormatInfo;
            }
        }

        public string NumberFormatString
        {
            get
            {
                return "F";
            }
        }

        /// <summary>
        ///     Interprets a series of flags as a binary representation
        ///     of an integer value.
        /// </summary>
        /// <param name="aFlags">
        ///     The list of flags to interpret, beginning with the most
        ///     significant bit.
        /// </param>
        /// <returns>
        ///     The integer value corresponding to the flags.
        /// </returns>

        public int ToBinary (params int[] aFlags)
        {
            int n = 0;
            foreach (int f in aFlags)
            {
                n = n << 2 + (_Flags[f] ? 1 : 0);
            }
            return n;
        }

        public bool UserMode
        {
            get
            {
                return this[FlagReference.UserMode];
            }
            set
            {
                this[FlagReference.UserMode] = value;
            }
        }

        public bool Alpha
        {
            get
            {
                return this[FlagReference.Alpha];
            }
            set
            {
                SetFlag (FlagReference.Alpha, value);
            }
        }

        public bool Shift
        {
            get
            {
                return this[FlagReference.Shift];
            }
            set
            {
                SetFlag (FlagReference.Shift, value);
            }
        }
    }
}
